package com.thrsdk.log;

import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import com.thrsdk.constants.AppConstants;
import com.thrsdk.log.interfaces.LogPrinterType;
import com.thrsdk.model.data.PayInfo;

import java.util.ArrayList;
import java.util.List;

public class LogUtils {
    public static String TAG = "dyfusionsdk_debug_log";
    private boolean logOpen = false;
    private boolean rootDebug = false;
    public static int stackDepth = 3;  //调用栈日志的深度， 3代表当前类当前方法，递增为调用类的层级的方法位置，递减则为虚拟机（本地方法），线程等层级日志
    private static LogUtils logUtils = null;
    private static LogPrinterType logPrinterType = null;
    private String methodInfo = "";
    private static List<String> methodList;
    private Handler mHandler;
    private Runnable mRunable;
    private final long mDeyle = 30*1000;
    private final long endTime = 10 * 60 *1000;
    private long timeCount = 0;
    public static final String SDK_APPINIT= "applicationInit";
    public static final String SDK_INIT = "initInterface";
    public static final String SDK_LOGIN = "login";
    public static final String SDK_PAYMENT = "payment";
    public static final String SDK_EXIT = "exit";
    public static final String SDK_GETCERTIF = "getCertificateData";
    public static final String SDK_SETUSERLISTER = "setUserListener";
    public static final String SDK_SWITHACCOUNT = "switchAccount";
    public static final String SDK_SETEXTDATA= "setExtData";
    public static final String SDK_ONSTART = "onStart";
    public static final String SDK_ONCREATE = "onCreate";
    public static final String SDK_ONSTOP = "onStop";
    public static final String SDK_ONDESTORY = "onDestroy";
    public static final String SDK_ONRESUME = "onResume";
    public static final String SDK_ONPAUSE = "onPause";
    public static final String SDK_ONRESTART = "onRestart";
    public static final String SDK_ONNEWINTENT = "onNewIntent";
    public static final String SDK_ONACTRESULT= "onActivityResult";
    public static final String SDK_ONKEYDOWN= "onKeyDown";
    public static final String SDK_CONFCHANGED = "onConfigurationChanged";
    public static final String SDK_ONREQPERRESULT = "onRequestPermissionsResult";

    public static LogUtils getInstance() {
        if (logUtils == null) {
            synchronized (LogUtils.class){
                if (logUtils == null) {
                    logUtils = new LogUtils();
                    methodList = new ArrayList<>();
                    addMethods(methodList);
                }
            }
        }
        return logUtils;
    }

    private static void addMethods(List<String> ms){
        ms.add(SDK_INIT);
        ms.add(SDK_LOGIN);
        ms.add(SDK_PAYMENT);
        ms.add(SDK_SETUSERLISTER);
        ms.add(SDK_SWITHACCOUNT);
        ms.add(SDK_SETEXTDATA);
        ms.add(SDK_EXIT);
        ms.add(SDK_ONSTART);
        ms.add(SDK_ONCREATE);
        ms.add(SDK_ONSTOP);
        ms.add(SDK_ONDESTORY);
        ms.add(SDK_ONRESUME);
        ms.add(SDK_ONPAUSE);
        ms.add(SDK_ONRESTART);
        ms.add(SDK_ONNEWINTENT);
        ms.add(SDK_ONACTRESULT);
        ms.add(SDK_ONKEYDOWN);
        ms.add(SDK_CONFCHANGED);
        ms.add(SDK_ONREQPERRESULT);
    }

    public void openRootDebug(boolean debug) {
        this.rootDebug = debug;
        openCheckMethod(debug);
    }

    public void openDebug(boolean debug) {
        this.logOpen = debug;
        if (debug) stackDepth = 5;
        openCheckMethod(debug);
        logPrinterType = LogPrinterFactory.getInstance().createType(0);
    }

    private void openCheckMethod(boolean debug){
        synchronized (this){
            if (debug && mHandler == null){
                mHandler = new Handler();
                mRunable = new Runnable() {
                    @Override
                    public void run() {
                        e(null, methodList.toArray());
                        timeCount += mDeyle;
                        if (timeCount < endTime) mHandler.postDelayed(mRunable, mDeyle);
                        else mHandler.removeCallbacks(mRunable);
                    }
                };
            }
        }
    }

    /**
     * 此方法无视是否开启日志模式，都打印出来
     *
     * @param priority  日志等级
     * @param tag       日志tag
     * @param throwable 日志异常捕获
     * @param args      日志内容参数
     */
    public void superLog(int priority, String tag, Throwable throwable, Object... args) {
        if (logPrinterType == null) logPrinterType = LogPrinterFactory.getInstance().createType(0);
        methodInfo = getMethonInfo(4);
        logPrinterType.superLog(priority, tag, throwable, args);
    }

    public void log(int priority, String tag, Throwable throwable, Object... args) {
        if (logOpen || rootDebug) {
            methodInfo = getMethonInfo(stackDepth);
            logPrinterType.log(priority, tag, throwable, args);
        }
    }

    public void v(Object... args) {
        if (logOpen || rootDebug) {
            methodInfo = getMethonInfo(stackDepth);
            logPrinterType.v("", args);
        }
    }

    public void d(Object... args) {
        if (logOpen || rootDebug) {
            methodInfo = getMethonInfo(stackDepth);
            logPrinterType.d("", args);
        }
    }

    public void i(Object... args) {
        if (logOpen || rootDebug) {
            methodInfo = getMethonInfo(stackDepth);
            logPrinterType.i("", args);
        }
    }

    public void w( Object... args) {
        if (logOpen || rootDebug) {
            methodInfo = getMethonInfo(stackDepth);
            logPrinterType.w("", args);
        }
    }

    public void e(Throwable throwable, Object... args) {
        if (logOpen || rootDebug) {
            methodInfo = getMethonInfo(stackDepth);
            logPrinterType.e("", throwable, args);
        }
    }

    private void checkThread() {
        if (Looper.getMainLooper().getThread() != Thread.currentThread()) throw new IllegalArgumentException("SDK方法请在UI主线程运行， 方法调用信息：" + getMethonInfo(stackDepth+1));
    }

    public void checkSDKMethod(String name, Object... objects){
        checkThread();
        if (methodList != null) methodList.remove(name);
        switch (name){
            case SDK_INIT:
                if (!AppConstants.alreadyLoadApplication) throw new IllegalArgumentException("没有继承YXApplication 或 YXApplication的onCreate未执行");
                if (objects[0] == null ||  TextUtils.isEmpty(objects[2].toString()) || objects[3] == null) throw new IllegalArgumentException("初始化方法initInterface参数有误，请检查！");
                d("appid:"+( objects[1]), "appkey:"+objects[2].toString(),
                        "context is null:" + (objects[0] == null), "listener is null:" + (objects[3] == null));
                break;
            case SDK_LOGIN:
                if (objects[0] == null ||  TextUtils.isEmpty(objects[2].toString()) || objects[3] == null)  throw new IllegalArgumentException("登录方法login必要参数缺失，请检查！");
                break;
            case SDK_PAYMENT:
                PayInfo payInfo = (PayInfo)objects[1];
                LogUtils.getInstance().d(payInfo);
                if(TextUtils.isEmpty(payInfo.getProductName()) || TextUtils.isEmpty(payInfo.getOrderid()) || payInfo.getAmount().contains(".")){
                    throw new IllegalArgumentException("payment方法中PaymentInfo必要参数缺失,请检查！  ==>检查方式：subject参数不能空 或 billno不能为空 或 amount只能为整形字符串");
                }
                break;
            case SDK_SETUSERLISTER:
                break;
            case SDK_SWITHACCOUNT:
                break;
            case SDK_SETEXTDATA:
                d("context is null:" + (objects[0] == null), objects[1]);
                break;
            case SDK_EXIT:
                d("context is null:" + (objects[0] == null), "listener is null:" + (objects[1]  == null));
                break;
            case SDK_ONSTART:
                d("onStart");
                break;
            case SDK_ONCREATE:
                d("onCreate");
                break;
            case SDK_ONSTOP:
                d("onStop");
                break;
            case SDK_ONDESTORY:
                d("onDestory");
                break;
            case SDK_ONRESUME:
                d("onResume");
                break;
            case SDK_ONPAUSE:
                d("onPause");
                break;
            case SDK_ONRESTART:
                d("onRestart");
                break;
            case SDK_ONNEWINTENT:
                d("onNewIntent");
                break;
            case SDK_ONACTRESULT:
                d("onActivityResult");
                break;
            case SDK_ONKEYDOWN:
                d("onKeyDown");
                break;
            case SDK_CONFCHANGED:
                d("onConfigurationChanged");
                break;
            case SDK_ONREQPERRESULT:
                d("onRequestPermissionsResult");
                break;
        }
    }

    /**
     * Formats the given json content and print it
     */
    public void json(String json) {
        methodInfo = getMethonInfo(stackDepth);
        logPrinterType.json("", json);
    }

    /**
     * Formats the given xml content and print it
     */
    public void xml(String xml) {
        methodInfo = getMethonInfo(stackDepth);
        logPrinterType.xml("", xml);
    }

//    public void printParmas(String... pl){
//        if (pl.length > 0&&logOpen){
//            for (String p: pl){
//
//            }
//        }
//    }

    public String getMethodMsg() {
        return this.methodInfo;
    }

    private String getMethonInfo(int pos) {
        StringBuilder builder = new StringBuilder();
        StackTraceElement[] elements = Thread.currentThread().getStackTrace();
//        for (StackTraceElement ste : elements) {
//            if (ste.isNativeMethod() || ste.getClassName().equals(Thread.class.getName())
//                    || ste.getClassName().equals(this.getClass().getName())) {
//                continue;
//            }
        builder.append(PrinterFormat.DOUBLE_SPACE)
                .append("Thread:").append(Thread.currentThread().getName()).append("\n")
                .append(PrinterFormat.DOUBLE_SPACE)
                .append("Method:").append(elements[pos].getMethodName()).append("\n")
                .append(PrinterFormat.DOUBLE_SPACE)
                .append("Class:").append(elements[pos].getClassName()).append("\n")
                .append(PrinterFormat.DOUBLE_SPACE)
                .append("Path:").append(elements[pos].getClassName()).append(".")
                .append(elements[pos].getMethodName())
                .append("(")
                .append(elements[pos].getFileName())
                .append(":").append(elements[pos].getLineNumber())
                .append(")").append("\n");
        return builder.toString();
    }
}
